package com.zodiac.alioth.common.api;

import com.alibaba.fastjson.JSON;
import org.springframework.util.ReflectionUtils;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by well on 2014/7/29.
 */
public class ReflectTool {

    public static Object resolveField(Class<?> clazz, Map<String, Object> map, Object val) throws InstantiationException, IllegalAccessException, ParseException {
        if (isSimpleClass(clazz)) {
            return resolveSimpleVal(clazz, val);
        } else {
            return resolveCustomField(map, clazz);
        }
    }

    public static boolean isSimpleClass(Class<?> clazz) {
        if (clazz == null) {
            return true;
        }
        return Integer.class.isAssignableFrom(clazz)
                || int.class.isAssignableFrom(clazz)
                || Long.class.isAssignableFrom(clazz)
                || long.class.isAssignableFrom(clazz)
                || Number.class.isAssignableFrom(clazz)
                || Float.class.isAssignableFrom(clazz)
                || float.class.isAssignableFrom(clazz)
                || Double.class.isAssignableFrom(clazz)
                || double.class.isAssignableFrom(clazz)
                || Character.class.isAssignableFrom(clazz)
                || char.class.isAssignableFrom(clazz)
                || Short.class.isAssignableFrom(clazz)
                || short.class.isAssignableFrom(clazz)
                || String.class.isAssignableFrom(clazz)
                || boolean.class.isAssignableFrom(clazz)
                || Boolean.class.isAssignableFrom(clazz)
                || Date.class.isAssignableFrom(clazz)
                || List.class.isAssignableFrom(clazz)
                || Set.class.isAssignableFrom(clazz)
                || String[].class.isAssignableFrom(clazz)
                || int[].class.isAssignableFrom(clazz)
                || Integer[].class.isAssignableFrom(clazz)
                || long[].class.isAssignableFrom(clazz)
                || Long[].class.isAssignableFrom(clazz)
                || byte[].class.isAssignableFrom(clazz)
                || Byte[].class.isAssignableFrom(clazz)
                || float[].class.isAssignableFrom(clazz)
                || Float[].class.isAssignableFrom(clazz)
                || double[].class.isAssignableFrom(clazz)
                || Double[].class.isAssignableFrom(clazz)
                || short[].class.isAssignableFrom(clazz)
                || Short[].class.isAssignableFrom(clazz)
                || Number[].class.isAssignableFrom(clazz);
    }

    public static Object resolveCustomField(final Map<String, Object> map, Class<?> clazz) throws IllegalAccessException, InstantiationException {
        final Object obj = clazz.newInstance();
        ReflectionUtils.doWithFields(clazz, field -> {
            String fieldName = field.getName();
            Object val = map.get(fieldName);
            try {
                Object o = resolveSimpleVal(field.getType(), val);
                if(o != null){
                    ReflectionUtils.makeAccessible(field);
                    field.set(obj, o);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        return obj;
    }

    public static Object resolveSimpleVal(Class<?> paramTypeClass, Object val) throws ParseException {
        if (val == null || paramTypeClass == null) {
            return null;
        }
        if (String.class.isAssignableFrom(paramTypeClass)) {
            return val.toString();
        } else if (int.class.isAssignableFrom(paramTypeClass)) {
            return new Integer(val.toString());
        } else if (Integer.class.isAssignableFrom(paramTypeClass)) {
            return new Integer(val.toString());
        } else if (Long.class.isAssignableFrom(paramTypeClass)) {
            return new Long(val.toString());
        } else if (double.class.isAssignableFrom(paramTypeClass)) {
            return Double.parseDouble(val.toString());
        } else if (Double.class.isAssignableFrom(paramTypeClass)) {
            return new Double(val.toString());
        } else if (float.class.isAssignableFrom(paramTypeClass)) {
            return Float.parseFloat(val.toString());
        } else if (Float.class.isAssignableFrom(paramTypeClass)) {
            return new Float(val.toString());
        } else if (long.class.isAssignableFrom(paramTypeClass)) {
            return Long.parseLong(val.toString());
        } else if (short.class.isAssignableFrom(paramTypeClass)) {
            return Short.parseShort(val.toString());
        } else if (Short.class.isAssignableFrom(paramTypeClass)) {
            return new Short(val.toString());
        } else if (BigDecimal.class.isAssignableFrom(paramTypeClass)) {
            return new BigDecimal(val.toString());
        } else if (Character.class.isAssignableFrom(paramTypeClass)) {
            if (val.toString().length() != 1) {
                throw new RuntimeException("convert data error type:[" + paramTypeClass + "],with value:[" + val + "]");
            }
            return val.toString().charAt(0);
        } else if (char.class.isAssignableFrom(paramTypeClass)) {
            if (val.toString().length() != 1) {
                throw new RuntimeException("convert data error type:[" + paramTypeClass + "],with value:[" + val + "]");
            }
            return val.toString().charAt(0);
        } else if (Date.class.isAssignableFrom(paramTypeClass)) {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
            return simpleDateFormat.parse(val.toString());
        } else if (List.class.isAssignableFrom(paramTypeClass)) {
        	if (val.toString().startsWith("[") && val.toString().endsWith("]")) {
        		return JSON.parseArray(val.toString(), Object.class);
        	}
            String[] array = val.toString().split(",");
            return Arrays.asList(array);
        } else if (Set.class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return new HashSet<>(Arrays.asList(array));
        } else if (String[].class.isAssignableFrom(paramTypeClass)) {
            return val.toString().split(",");
        } else if (int[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2intArray(array);
        } else if (Integer[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2IntegerArray(array);
        } else if (long[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2longArray(array);
        } else if (Long[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2LongArray(array);
        } else if (double[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2doubleArray(array);
        } else if (Double[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2DoubleArray(array);
        } else if (float[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2floatArray(array);
        } else if (Float[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2FloatArray(array);
        } else if (short[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2shortArray(array);
        } else if (Short[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2ShortArray(array);
        } else if (byte[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2byteArray(array);
        } else if (Byte[].class.isAssignableFrom(paramTypeClass)) {
            String[] array = val.toString().split(",");
            return convert2ByteArray(array);
        } else {
            throw new RuntimeException("cannot find convert class type:[" + paramTypeClass + "],with value:[" + val + "]");
        }

    }

    private static int[] convert2intArray(String[] array) {
        if (array == null) {
            return null;
        }
        int[] target = new int[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = new Integer(array[i]);
        }
        return target;
    }

    private static Integer[] convert2IntegerArray(String[] array) {
        if (array == null) {
            return null;
        }
        Integer[] target = new Integer[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = new Integer(array[i]);
        }
        return target;
    }

    private static long[] convert2longArray(String[] array) {
        if (array == null) {
            return null;
        }
        long[] target = new long[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = new Long(array[i]);
        }
        return target;
    }

    private static Long[] convert2LongArray(String[] array) {
        if (array == null) {
            return null;
        }
        Long[] target = new Long[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = new Long(array[i]);
        }
        return target;
    }

    private static double[] convert2doubleArray(String[] array) {
        if (array == null) {
            return null;
        }
        double[] target = new double[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = Double.parseDouble(array[i]);
        }
        return target;
    }

    private static Double[] convert2DoubleArray(String[] array) {
        if (array == null) {
            return null;
        }
        Double[] target = new Double[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = Double.valueOf(array[i]);
        }
        return target;
    }

    private static float[] convert2floatArray(String[] array) {
        if (array == null) {
            return null;
        }
        float[] target = new float[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = Float.parseFloat(array[i]);
        }
        return target;
    }

    private static Float[] convert2FloatArray(String[] array) {
        if (array == null) {
            return null;
        }
        Float[] target = new Float[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = Float.valueOf(array[i]);
        }
        return target;
    }

    private static short[] convert2shortArray(String[] array) {
        if (array == null) {
            return null;
        }
        short[] target = new short[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = Short.parseShort(array[i]);
        }
        return target;
    }

    private static Short[] convert2ShortArray(String[] array) {
        if (array == null) {
            return null;
        }
        Short[] target = new Short[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = Short.valueOf(array[i]);
        }
        return target;
    }
    private static byte[] convert2byteArray(String[] array) {
        if (array == null) {
            return null;
        }
        byte[] target = new byte[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = Byte.parseByte(array[i]);
        }
        return target;
    }

    private static Byte[] convert2ByteArray(String[] array) {
        if (array == null) {
            return null;
        }
        Byte[] target = new Byte[array.length];
        for (int i = 0, len = array.length; i < len; ++i) {
            target[i] = Byte.valueOf(array[i]);
        }
        return target;
    }





}
